﻿package net.wangit.framework.util;

import net.wangit.MapList;
import net.wangit.Row;
import net.wangit.Unit;
import net.wangit.context.ActionContext;
import net.wangit.context.ActionContextHelper;
import net.wangit.framework.session.AgileSession;
import net.wangit.framework.session.UnitSettings;
import net.wangit.jdbc.DBImplementor;
import net.wangit.jdbc.Type;
import net.wangit.jdbc.database.Database;
import net.wangit.jdbc.table.Column;
import net.wangit.jdbc.table.TableMeta;
import net.wangit.jdbc.table.TableMetaFactory;
import net.wangit.util.Checker;
import net.wangit.util.StringUtil;
import net.wangit.view.element.ElementComponent;
import net.wangit.view.element.component.MultiSelectFieldElement;
import net.wangit.view.element.util.ElementUtil;
import net.wangit.view.unit.UnitComponent;
import net.wangit.view.unit.UnitConfigurationException;
import net.wangit.view.unit.UnitFactory;
import net.wangit.view.unit.component.QueryUnit;

public class QueryProcessor {

	private static final String NULL_CONDITION = " 1=2 and ";

	public QueryProcessor() {
	}

	public static String addConditionAndOrder(ActionContext ac,
			DBImplementor db, String sql, String domain, String queryUnitId,
			String tableName, String tableAlias) {
		UnitComponent qu = UnitFactory.getUnitWithoutClone(domain, queryUnitId);
		if (!(qu instanceof QueryUnit))
			throw new UnitConfigurationException("QueryUnit is excepted! ",
					domain, queryUnitId, "queryUnitId");
		AgileSession session = ActionContextHelper.getSession(ac);
		UnitSettings us = session.getUnitSettings(domain, qu.getId());
		MapList data = us.getData();
		Row row = data != null ? data.getRow(0) : null;
		TableMeta meta = TableMetaFactory.getTableMeta(domain, tableName);
		String limitSql = getLimitSql(ac, db, qu, meta, tableAlias, row);
		String orderSql = getOrderSql((QueryUnit) qu, row);
		if (!Checker.isEmpty(limitSql))
			sql = addCondition(sql, limitSql);
		if (!Checker.isEmpty(orderSql))
			sql = addOrder(sql, orderSql);
		return sql;
	}

	private static String getLimitSql(ActionContext ac, DBImplementor db,
			Unit unit, TableMeta meta, String tableAlias, Row row) {
		if (unit.size() == 0)
			return null;
		Database database = db.getJDBCSettings().getDatabase();
		StringBuffer limitSql = new StringBuffer();
		for (int i = 0; i < unit.size(); i++) {
			ElementComponent ec = (ElementComponent) unit.getElement(i);
			if (!ec.isToolbarElement()) {
				String columnName = ec.getId();
				Column column = meta.getColumn(columnName);
				if (column != null) {
					String value = ElementUtil.getValueOrDefault(ac, ec, row);
					int queryMode = ec.getQueryMode();
					String valueTo = row != null ? row.get((new StringBuilder(
							String.valueOf(columnName))).append(".t")
							.toString()) : null;
					if (ec.isRequired()) {
						if (queryMode == 4
								&& (Checker.isEmpty(value) || Checker
										.isEmpty(valueTo)))
							return " 1=2 and ";
						if (queryMode != 4 && Checker.isEmpty(value))
							return " 1=2 and ";
					}
					String subSql = null;
					Type type = column.getType();
					if (queryMode == 4
							&& (!Checker.isEmpty(value) || !Checker
									.isEmpty(valueTo)))
						subSql = database.getScopeQueryOption(type, tableAlias,
								columnName, value, valueTo);
					else if (queryMode != 4 && !Checker.isEmpty(value))
						if (ec instanceof MultiSelectFieldElement) {
							String values[] = StringUtil.split(value, ",");
							subSql = getMultiValueLimit(database, type,
									tableAlias, columnName, values);
						} else {
							boolean isEqual = isEqual(queryMode, columnName,
									row);
							subSql = database.getQueryOption(type, tableAlias,
									columnName, value, isEqual);
						}
					if (!Checker.isEmpty(subSql))
						limitSql.append(" ").append(subSql).append(" and ");
				}
			}
		}

		return limitSql.toString();
	}

	private static String getMultiValueLimit(Database database, Type type,
			String tableAlias, String columnName, String values[]) {
		boolean limited = false;
		StringBuffer sql = new StringBuffer("(");
		for (int i = 0; i < values.length; i++)
			if (!Checker.isEmpty(values[i])) {
				limited = true;
				sql.append(database.getQueryOption(type, tableAlias,
						columnName, values[i], true));
				if (i < values.length - 1)
					sql.append(" or ");
			}

		sql.append(")");
		return limited ? sql.toString() : null;
	}

	private static boolean isEqual(int queryMode, String columnName, Row row) {
		if (queryMode == 3)
			if (row == null)
				queryMode = 1;
			else
				queryMode = row.getInt((new StringBuilder(String
						.valueOf(columnName))).append(".p").toString(), 1);
		return queryMode != 2;
	}

	private static String addCondition(String sql, String limitSql) {
		String sqlLower = sql.toLowerCase();
		int index = sqlLower.indexOf(" where ");
		if (index > 0)
			return (new StringBuilder(String.valueOf(sql
					.substring(0, index + 7)))).append(limitSql).append(
					sql.substring(index + 7)).toString();
		index = sqlLower.indexOf(" group by ");
		if (index < 0)
			index = sqlLower.indexOf(" order by ");
		limitSql = (new StringBuilder(" where ")).append(
				limitSql.substring(0, limitSql.length() - 4)).toString();
		if (index > 0)
			return (new StringBuilder(String.valueOf(sql.substring(0, index))))
					.append(limitSql).append(sql.substring(index)).toString();
		else
			return (new StringBuilder(String.valueOf(sql))).append(limitSql)
					.toString();
	}

	private static String getOrderSql(QueryUnit uc, Row row) {
		String orderElementId = uc.getOrderElementId();
		if (Checker.isEmpty(orderElementId))
			return null;
		ElementComponent ec = uc.getElementComponent(orderElementId);
		String orderField = row != null ? row.get(orderElementId) : ec
				.getDefaultValue();
		boolean desc = row != null ? "2".equals(row.get((new StringBuilder(
				String.valueOf(orderElementId))).append(".o").toString()))
				: false;
		if (!Checker.isEmpty(orderField))
			return (new StringBuilder(" ")).append(orderField).append(
					desc ? " desc," : ",").toString();
		else
			return null;
	}

	private static String addOrder(String sql, String orderSql) {
		String sqlLower = sql.toLowerCase();
		int orderIndex = sqlLower.indexOf(" order by ");
		if (orderIndex > 0) {
			if (sql.substring(orderIndex).indexOf(orderSql) >= 0)
				return sql;
			else
				return (new StringBuilder(String.valueOf(sql.substring(0,
						orderIndex + 10)))).append(orderSql).append(
						sql.substring(orderIndex + 10)).toString();
		} else {
			orderSql = orderSql.substring(0, orderSql.length() - 1);
			return (new StringBuilder(String.valueOf(sql)))
					.append(" order by ").append(orderSql).toString();
		}
	}
}
